<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>

<body>
    <script>
        const PENDING = 'pending'
        const RESOLVED = 'resolved'
        const REJECTED = 'rejected'
        class MyPromise {
            status = ''
            result = ''
            handlers = []
            constructor(executor) {
                this.status = PENDING
                this.data = ''

                const resolve = (res) => {
                    this.changeStatus(RESOLVED, res)
                }
                const reject = (err) => {
                    this.changeStatus(REJECTED, err)
                }

                executor(resolve, reject)
            }

            changeStatus(status, result) {
                if (this.status === PENDING) {
                    this.status = status
                    this.result = result
                    this.run()
                }
            }

            isPromise(data) {
                return false
            }

            runMicroTask(func) {
                setTimeout(func, 0);
            }

            runOne(callback, resolve, reject) {
                this.runMicroTask(() => {
                    if (typeof callback !== 'function') {
                        const settle = this.status === RESOLVED ? resolve : reject
                        settle(this.result)
                        return
                    } else {
                        try {
                            const data = callback(this.result)
                            if (this.isPromise(data)) {
                                data.then(resolve, reject)
                            } else {
                                resolve(data)
                            }
                        } catch (e) {
                            reject(e)
                        }
                    }
                })
            }

            run() {
                if (this.status === PENDING) return

                while (this.handlers.length) {
                    const { onFulFilled, onRejected, resolve, reject } = this.handlers.shift()

                    if (this.status === RESOLVED) {
                        this.runOne(onFulFilled, resolve, reject)
                    }

                    if (this.status === REJECTED) {
                        this.runOne(onRejected, resolve, reject)
                    }
                }


            }

            then(onFulFilled, onRejected) {
                return new MyPromise((resolve, reject) => {
                    this.handlers.push({ onFulFilled, onRejected, resolve, reject })
                    this.run()
                })
            }
        }

        new MyPromise((resolve, reject) => {
            setTimeout(() => {
                resolve(123)
                reject(456)
            }, 1000);

        }).then(
            res => {
                console.log(res)
                return new Promise((resolve, reject) => {
                    resolve('mystic')
                })
            },
            err => {
                console.log(err)
                // return 777
            }
        ).then(
            res => {
                res.then(res => console.log(res))
            },
            err => {
                console.log(err)
            }
        )
    </script>
</body>

</html>